﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using GameEngine.EntityComponents;
using Microsoft.Xna.Framework;
using System.Diagnostics;
using GameEngine.Managers.IManagers;
using GameEngine.Camera;

namespace GameEngine.Entities
{
    public abstract class AbstractEntity
    {
        #region Fields

        public virtual Rectangle BoundingRectangle { get { return new Rectangle((int)position.X, (int)position.Y, (int)(texture.Width*scale), (int)(texture.Height*scale)); } set { } }

        public List<IEntityComponent> components;
        public List<AbstractEntity> children;
        public bool isActive = true;
        public bool isVisible = true;
        public Vector2 position;

        public Texture2D texture;
        public Game game;

        protected Rectangle sourceRectangle = Rectangle.Empty;
        protected float rotation = 0f;
        protected Vector2 origin = new Vector2();
        protected float scale = 1f;
        protected SpriteEffects effects;
        protected float layerDepth = 0f;

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractEntity" /> class.
        /// </summary>
        /// <param name="components">The components.</param>
        /// <param name="isActive">if set to <c>true</c> [is active].</param>
        /// <param name="isVisible">if set to <c>true</c> [is visible].</param>
        /// <param name="position">The position.</param>
        /// <param name="game">The game.</param>
        public AbstractEntity(List<IEntityComponent> components, bool isActive, bool isVisible, Vector2 position, Game game)
        {
            this.components = components;
            this.isActive = isActive;
            this.isVisible = isVisible;
            this.position = position;
            this.game = game;
            children = new List<AbstractEntity>();
            effects = new SpriteEffects();
        }

        public virtual void Update(GameTime gameTime)
        {
            if(this.isActive)
                UpdateComponents(gameTime);
        }

        /// <summary>
        /// Draws all the visible children using a BasicEffect
        /// </summary>
        /// <param name="basicEffect">The basic effect.</param>
        public virtual void Draw(BasicEffect basicEffect)
        {
            if (this.isVisible)
            {
                foreach (AbstractEntity entity in children)
                {
                    entity.Draw(basicEffect);
                }
            }
        }

        /// <summary>
        /// Draws all visible children.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch.</param>
        /// <param name="gameTime">The game time.</param>
        public virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            if (this.isVisible)
            {
                foreach (AbstractEntity entity in children)
                {
                    entity.Draw(spriteBatch, gameTime);
                }
            }
        }

        public abstract void LoadContent();

        /// <summary>
        /// Gets the component.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public IEntityComponent GetComponent(Type type)
        {
            foreach (IEntityComponent component in components)
            {
                if (component.GetType().Equals(type))
                    return component;
            }

            return null;
        }

        /// <summary>
        /// Adds a component.
        /// </summary>
        /// <param name="component">The component.</param>
        public void AddComponent(IEntityComponent component)
        {
            this.components.Add(component);
        }

        /// <summary>
        /// Updates the components.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        protected void UpdateComponents(GameTime gameTime)
        {
            foreach(IEntityComponent component in components)
            {
                component.Update(gameTime);
            }
        }

        /// <summary>
        /// Forwards the method call CollideWith on all components accociated with this AbstractEntity
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="isX">if set to <c>true</c> [is X].</param>
        /// <param name="isY">if set to <c>true</c> [is Y].</param>
        public void CollisionWith(AbstractEntity entity, bool isX, bool isY)
        {
            foreach (IEntityComponent component in components)
            {
                component.CollideWith(entity, isX, isY);
            }
        }
    }
}
